<template>
    <div>
      <div v-show="!isError" ref="chartRef" class="custom-chart"></div>
      <el-empty v-if="isError && !isDataScreen" :description="$t('common.base.noData')" style="height: 100%; width: 100%" />
    </div>
  </template>
  
  <script>
  /* vue */
  import { ref, watch, onMounted, onBeforeUnmount, nextTick, computed, defineComponent } from 'vue';
  /* components */
  import * as echarts from 'echarts';
  /* util */
  import { merge, debounce } from 'lodash';
  import { isOpenData } from '@src/util/platform';
  import NP from 'number-precision';
  
  export default defineComponent({
    props: {
      // 绘图数据
      data: {
        type: Array,
        default: () => [],
      },
      dataConfig: {
        type: Object,
        default: () => ({}),
      },
      // 图表类型
      chartType: {
        type: String,
        default: () => '',
      },
      // 重绘, 每次变动触发重绘
      redraw: {
        type: [Number, String],
        default: () => 0,
      },
      // 重新布局, 每次变动触发重新布局
      doLayout: {
        type: Number,
        default: () => 0,
      },
      // 图形是否可点击
      canClick: {
        type: Boolean,
        default: false,
      },
      // 在大屏内当前主题
      currentTheme: {
        type: Object,
        default: () => ({}),
      },
      // 是否是数据大屏
      isDataScreen: {
        type: Boolean,
        default: false,
      },
    },
    setup(props, { emit }) {
      // 是否进行边界检查，默认开启
      NP.enableBoundaryChecking(false);
      const chartRef = ref();
      const isError = ref(false);
      let ex = null;
      let percentage = 0;
  
      // 处理企微人员
      const handleStaffData = async data => {
        if (!isOpenData) {
          return;
        }
  
        if (window.WWOpenData?.initCanvas) {
          window.WWOpenData.initCanvas();
          window.WWOpenData.enableCanvasSharing();
        }
  
        const promises = [];
  
        for (const i of data) {
          const list = i.data;
          for (let index = 0; index < list.length; index++) {
            if (!list[index].staffId) continue;
            let items = [{ type: 'userName', id: list[index].staffId }];
            const result = await new Promise((resolve, reject) => {
              window.WWOpenData.prefetch({ items }, (err, data) => {
                if (err) {
                  return reject(err);
                }
                resolve(data);
              });
            });
            try {
              list[index] = Object.assign(list[index], { displayName: result.items[0]?.data });
            } catch (error) {
              console.warn(error);
            }
          }
          promises.push(list);
        }
  
        return Promise.all(promises.flat());
      };
  
      // 生成携带的维度指标数据
      const createField = field => {
        const field_ = {};
        for (const k in field) {
          k !== 'data' && (field_[k] = field[k]);
        }
        return field_;
      };
  
      // 处理数据
      const filterData = async () => {
        try {
          await handleStaffData(props.data);
        } catch (err) {
          console.error(err);
        }
  
        const data = {
          dims: [],
          indexs: [],
          aXios: new Set(),
          aXiosObj: {},
          aXiosOrder: {},
          yAxisL: false,
          yAxisR: false,
        };
        const dataObj = {};
        props.data.forEach(i => {
          dataObj[i.name] = i;
        });
        props.dataConfig?.dimensions?.forEach(i => data.dims.push(dataObj[i.displayName]));
        props.dataConfig?.indexs?.forEach(i => {
          dataObj[i.indexName].yAxisIndex = i.axisName === 'Y2' ? 1 : 0;
          i.axisName === 'Y1' && (data.yAxisL = true);
          i.axisName === 'Y2' && (data.yAxisR = true);
          data.indexs.push(dataObj[i.indexName]);
        });
        props.dataConfig?.fxIndexs?.forEach(i => {
          dataObj[i.fxIndexName].yAxisIndex = i.axisName === 'Y2' ? 1 : 0;
          i.axisName === 'Y1' && (data.yAxisL = true);
          i.axisName === 'Y2' && (data.yAxisR = true);
          data.indexs.push(dataObj[i.fxIndexName]);
        });
        data.dims[0] &&
          data.dims[0].data.forEach(i => {
            data.aXios.add(i.key);
            data.aXiosObj[i.key] = i.displayName;
          });
        data.aXios = Array.from(data.aXios);
        data.aXios.forEach((i, j) => (data.aXiosOrder[i] = j));
        // 目标值
        if (props.dataConfig.openCustomData) {
          data.customDataType = props.dataConfig.customDataType || 'single';
          data.customDataConfig = props.dataConfig.customDataConfig || [];
        }
        return data;
      };
  
      // 柱形图
      const bar = filterData => {
        const seriesObj = {};
        const series = [];
        filterData.dims[0].data.forEach((i, ii) => {
          let serieItemName = '';
          const dim = [{ field_: createField(filterData.dims[0]), value_: i, isAxisX: true }];
          filterData.dims.forEach((j, ji) => {
            if (+ji > 0) {
              serieItemName += serieItemName ? `,${j.data[ii].displayName || '空'}` : `${j.data[ii].displayName || '空'}`;
              dim.push({ field_: createField(j), value_: j.data[ii], isAxisX: false });
            }
          });
          filterData.indexs.forEach(j => {
            const curSerieItemName = serieItemName ? `${serieItemName},${j.name}` : j.name;
            if (!seriesObj[curSerieItemName]) {
              seriesObj[curSerieItemName] = {
                name: curSerieItemName,
                stack: j.name,
                type: 'bar',
                yAxisIndex: j.yAxisIndex,
                data: new Array(filterData.aXios.length).fill(''),
              };
              // 大屏样式
              if (props.isDataScreen) {
                seriesObj[curSerieItemName].itemStyle = {
                  borderRadius: 8,
                };
                seriesObj[curSerieItemName].itemStyle.barMinWidth = '20px';
              }
            }
            seriesObj[curSerieItemName].data[filterData.aXiosOrder[i.key]] = {
              value: j.data[ii].displayName,
              dim: [...dim, { field_: createField(j), value_: j.data[ii], isAxisX: false }],
            };
          });
        });
        for (const i in seriesObj) {
          seriesObj[i].data.forEach((j, ji) => {
            !j && (seriesObj[i].data[ji] = { value: '', dim: [] }); // 空值填充
          });
          series.push(seriesObj[i]);
        }
        // 目标值
        if (filterData.customDataType === 'single') {
          for (let index = 0; index < filterData.customDataConfig.length; index++) {
            const item = filterData.customDataConfig[index];
            series.push({
              name: item.indexName + '目标值',
              symbol: 'circle',
              type: 'line',
              smooth: true,
              itemStyle: {
                normal: {
                  lineStyle: {
                    width: 0.1,
                    type: 'dashed',
                  },
                },
              },
              data: Array(filterData.aXios.length).fill(parseFloat(item.customValue)),
              // 使用标线
              markLine: {
                silent: true,
                symbol: 'none',
                lineStyle: {
                  width: 2,
                  type: 'dashed',
                  shadowColor: 'rgba(255, 255, 255, 1)',
                  shadowBlur: 2,
                },
                label: {
                  position: 'insideStartTop',
                  color: 'inherit',
                },
                data: [{ yAxis: item.customValue }],
              },
            });
          }
        } else if (filterData.customDataType === 'many') {
          // 多目标值
          for (let index = 0; index < filterData.customDataConfig.length; index++) {
            const item = filterData.customDataConfig[index];
            const customValues = item.customValues.map(customValue => {
              const value = Object.values(customValue)[0] || 0;
              return parseFloat(value);
            });
            series.push({
              name: item.indexName + '目标值',
              symbol: 'circle',
              showSymbol: true,
              type: 'line',
              smooth: true,
              itemStyle: {
                normal: {
                  lineStyle: {
                    width: 2,
                    type: 'dashed',
                    shadowColor: 'rgba(255, 255, 255, 1)',
                    shadowBlur: 2,
                  },
                },
              },
              data: customValues,
            });
          }
        }
        return series;
      };
  
      const chartFn = {
        bar: bar,
      };
  
      // 生成 echarts 配置
      const chartOption = async () => {
        const color = ['#5B8FF9', '#F6903D', '#7262FD', '#F08BB4', '#61DDAA', '#F6BD16', '#65789B', '#D3CEFD', '#008685', '#A9C5FF', '#FBE5A2', '#5F94FF', '#9661BC', '#BFA6EB', '#BDEFDB', '#FCCAA1', '#5DC6F1', '#58BBBB', '#C2C8D5', '#FFD6E7'];
        const data = await filterData();
        // 初始化 option
        const option = {
          title: {},
          color: color,
          legend: {
            show: props.dataConfig.choiceLegend,
            // show: false,
            type: 'scroll',
          },
          tooltip: {
            confine: true,
            show: true,
            formatter: function (params) {
              let result = params.data.dim?.reduce((pre, cur) => (pre += `${cur.field_.name}: ${cur.value_.displayName}${cur.dataType === 'percent' ? '%' : ''}<br />`), '');
              if (params.seriesType === 'pie') {
                if (data.customDataType === 'single') {
                  if (params.data.targetValue && params.data.targetValue != 0) {
                    const percent = NP.times(NP.round(NP.divide(parseFloat(params.data.value), parseFloat(params.data.targetValue)), 4), 100) + '%';
                    result += `目标完成: ${percent}`;
                  } else {
                    result += `目标完成: -`;
                  }
                } else if (data.customDataType === 'many') {
                  // 多目标值
                  if (params.data.targetValue && params.data.targetValue != 0) {
                    const percent = NP.times(NP.round(NP.divide(parseFloat(params.data.value), parseFloat(params.data.targetValue)), 4), 100) + '%';
                    result += `目标完成: ${percent}`;
                  } else {
                    result += `目标完成: -`;
                  }
                }
              }
              return result;
            },
          },
          // dataZoom: [{ start: 0, end: 100 }],
          
          series: [],
          // yAxis: [
          //   { position: 'left', type: 'value', show: false, name: props.dataConfig.yAxisName1 },
          //   { position: 'right', type: 'value', show: false, name: props.dataConfig.yAxisName2 },
          // ],
          yAxis: [
            { position: 'left', type: 'value', show: false, name: props.dataConfig.yAxisName1,axisLine: {show: false},axisLabel:{show: false},axisTick:{show:false}, },
            { position: 'right', type: 'value', show: false, name: props.dataConfig.yAxisName2,axisLine: {show: false},axisLabel:{show: false},axisTick:{show:false}, },
          ],
          xAxis: [{ type: 'category', name: props.dataConfig.xAxisName, data: [] },],
        //   xAxis: [{ type: 'category', name: props.dataConfig.xAxisName, data: [], axisLine: { show: false }, axisLabel:{show: false},axisTick:{show:false},}],
          animationDelay: 300,
          grid: {
            left: '12px',
            right: '12px',
            top: '15%',
            bottom: '30px',
            containLabel: false,
          }
        };
  
        // if (props.chartType !== 'pie') {
        //   option.tooltip = {
        //     show: true,
        //     trigger: 'item',
        //     formatter: '{a} <br/>{b} : {c}',
        //   };
        // }
        option.series = chartFn[props.chartType](data);
        option.xAxis[0].data = data.aXios.map(i => data.aXiosObj[i]);
        option.yAxis[0].show = data.yAxisL;
        option.yAxis[1].show = data.yAxisR;
  
        if (props.isDataScreen) {
          option.title.text = props.dataConfig?.chartName || '';
  
          option.xAxis[0].axisLine = { show: false };
          option.xAxis[0].axisTick = { show: false };
  
          option.yAxis[0].show = false;
          option.yAxis[1].show = false;
        }
  
        option.series.forEach(i => {
          if (props.chartType !== 'pie') {
            i.label = {
              show: props.dataConfig?.choiceNumber || false,
            //   show: false,
              position: 'top',
              formatter: params => {
                if (params.data.dim && params.data.dim.length) {
                  return params.data.dim?.[params.data.dim.length - 1].dataType === 'percent' ? `${params.value}%` : `${params.value}`;
                } else {
                  return '';
                }
              },
            };
          }
  
          if (props.chartType === 'pie') {
            i.label = {
              formatter: params => {
                const label = [];
                params.data.dim.forEach((i, j) => {
                  if (props.dataConfig?.choiceNumber) {
                    label.push(i.value_.displayName);
                  } else if (+j !== params.data.dim.length - 1) {
                    label.push(i.value_.displayName);
                  }
                });
                if (params.data.dim && params.data.dim.length) {
                  return params.data.dim[params.data.dim.length - 1].dataType === 'percent' ? `${label.join(',')}%` : `${label.join(',')}`;
                } else {
                  return label.join(',');
                }
              },
            };
          }
  
          if (props.isDataScreen) {
            i.label.color = props.currentTheme?.color || '#fff';
          }
        });
  
        if (props.chartType === 'pie') {
          option.dataZoom[0].show = false;
          option.xAxis.forEach(i => (i.show = false));
          if (option.legend.show) {
            // 防止标签过多遮挡图例
            option.series.forEach(i => {
              i.top = '30';
            });
          }
        }
  
        if (props.isDataScreen) {
          // 柱状图超出12条数据轮播图形式展示
          if (isBarChartScroll.value) {
            const dataLength = props.data[0]?.data?.length;
            const indexsLength = props.dataConfig?.indexs?.length;
            percentage = Math.floor(100 / Math.ceil(dataLength / isScrollChart.bar) / indexsLength);
  
            option.dataZoom[0] = {
              xAxisIndex: 0,
              show: false,
              start: 0,
              end: percentage,
            };
          }
  
          option.legend.show = false;
  
          return Promise.resolve(merge(initOptions(props.currentTheme), option));
        }
  
        return Promise.resolve(option);
      };
  
      // 下载图片
      const download = name => {
        const src = ex.getDataURL({
          pixelRatio: 2,
          backgroundColor: '#fff',
          excludeComponents: ['dataZoom'],
        });
        const aEl = document.createElement('a');
        aEl.setAttribute('href', src);
        aEl.setAttribute('download', name || '图片名称');
        aEl.setAttribute('target', '_blank');
        aEl.click();
      };
  
      // 图形点击事件
      const clickEvent = params => {
        emit('clickEvent', params, 'chart');
      };
  
      // 图例点击事件
      const legendEvent = () => {
        emit('clickLegend');
      };
  
      // 重新布局
      const resize = () => {
        ex && ex.resize();
      };
  
      // 控制检查, 不通过时不绘制图形
      const createIsError = () => {
        let isError_ = false;
        if (!props.data || !props.data.length || !ex) {
          isError_ = true;
          return isError_;
        }
  
        props.data.forEach(i => {
          if (!i.data || !i.data.length) {
            isError_ = true;
          } else {
            isError_ = false;
          }
        });
  
        if (isError_) {
          return isError_;
        }
  
        return isError_;
      };
  
      let option = {};
  
      // 绘制
      const draw = async () => {
        if (createIsError()) {
          isError.value = true;
          return;
        }
  
        isError.value = false;
        option = await chartOption();
        ex.setOption(option, {
          notMerge: true,
        });
  
        if (props.isDataScreen) {
          barChartScroll();
        }
  
        resize();
      };
  
      /* -------------------------- 数据大屏模块内容 start -------------------------- */
      let barScrollTime = null;
      // 图表需要滚动的数值
      const isScrollChart = {
        bar: 12,
      };
  
      // 柱状图是否需要滚动
      const isBarChartScroll = computed(() => {
        return props.chartType === 'bar' && props.data[0]?.data?.length > isScrollChart.bar;
      });
  
      // 大屏模式下初始化样式
      const initOptions = (style = {}) => {
        const color = style?.color || '#fff';
        const lineStyle = { color };
  
        return {
          title: {
            text: '',
            top: '15px',
            left: '15px',
            textStyle: {
              fontWeight: 'normal',
              fontSize: 20,
              color,
            },
          },
          dataZoom: [
            {
              show: false,
            },
          ],
          legend: {
            top: '15px',
            right: '15px',
            textStyle: {
              color,
              fontSize: '14px',
            },
          },
          grid: {
            left: '7%',
            right: '20px',
            top: '25%',
            bottom: '30px',
            containLabel: false,
          },
          xAxis: [
            {
              axisLine: {
                lineStyle,
              },
            },
          ],
          yAxis: [
            {
              axisLine: {
                show: false,
                lineStyle,
              },
            },
            {
              axisLine: {
                show: false,
                lineStyle,
              },
            },
          ],
        };
      };
  
      // 多柱状图数据滚动展示
      function barChartScroll() {
        if (isBarChartScroll.value) {
          barScrollTime = setInterval(() => {
            if (!option.dataZoom && props.chartType !== 'bar') return;
            // 每次向后滚动一个，最后一个从头开始。
            if (option.dataZoom[0].end >= 100) {
              option.dataZoom[0].start = 0;
              option.dataZoom[0].end = percentage;
            } else {
              option.dataZoom[0].start = option.dataZoom[0].end;
              option.dataZoom[0].end += percentage;
            }
            ex.setOption(option);
          }, 3000);
        }
      }
      /* -------------------------- 数据大屏模块内容 end -------------------------- */
  
      watch(() => props.redraw, draw);
  
      watch(
        () => isError.value,
        () => {
          if (!isError.value) {
            nextTick(resize);
          }
        }
      );
  
      watch(
        () => props.doLayout,
        () => {
          resize();
          console.log('重新布局');
        }
      );
  
      onMounted(() => {
        if (!chartRef.value) {
          return;
        }
  
        ex = echarts.init(chartRef.value);
  
        if (props.canClick) {
          ex.on('click', clickEvent);
          ex.on('legendselectchanged', legendEvent);
        }
  
        if (props.data) {
          draw();
        }
  
        window.addEventListener('resize', resize);
      });
  
      onBeforeUnmount(() => {
        ex && ex.dispose();
        window.removeEventListener('resize', resize);
        barScrollTime && clearInterval(barScrollTime);
      });
  
      // defineExpose({
      //   // 下载图片
      //   download,
      // });
      return {
        chartRef,
        isError,
        ex,
        percentage,
        handleStaffData,
        createField,
        filterData,
        bar,
        chartFn,
        chartOption,
        download,
        clickEvent,
        legendEvent,
        resize,
        createIsError,
        option,
        draw,
        barScrollTime,
        isScrollChart,
        isBarChartScroll,
        initOptions,
        barChartScroll,
      };
    },
  });
  </script>
  
  <style lang="scss" scoped>
  .custom-chart {
    width: 100%;
    height: 100%;
    min-height: 276px;
  }
  </style>
  